import os
import random
from math import ceil
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
import arrow
from flask import current_app as app
from flask.helpers import make_response


class Captcha:
    type = 'number'
    key = 'imageCaptcha'
    expire_key = 'lastImageCaptchaTime'

    def __init__(self, expires=300, rate=0.8, img_width=86, img_height=32, answer=''):
        """
        初始化,设置各种属性
        """
        self.expires = expires

        # 验证码图片尺寸
        self.rate = 0.8
        self.img_width = 86
        self.img_height = 32

        self.answer = answer
        self.captcha = None

    def set_expires(self, expire):
        self.expires = expire

    def _get_font_size(self):
        """ 字体大小"""
        s1 = int(self.img_height*1.2)
        s2 = int(self.img_width/len(self.code))
        return int(min((s1, s2)) + max((s1, s2))*0.01)

    # 数字公式验证码

    def number(self):
        m, n = 1, 20
        x = random.randrange(m, n)
        y = random.randrange(m, n)

        r = random.randrange(0, 2)
        if x < 10 and y < 10:
            code = "%s x %s = ?" % (x, y)
            z = x * y
        elif r == 0:
            x, y = max(x, y), min(x, y)
            code = "%s - %s = ?" % (x, y)
            z = x - y
        else:
            code = "%s + %s = ?" % (x, y)
            z = x + y
        self.answer = z
        return code

    def _yield_code(self):
        return eval(f'self.{self.type.lower()}')()

    def display(self):
        """
        验证码生成
        """

        # 验证码字体颜色
        self.font_color = ['goldenrod', 'teal',
                           'chocolate', 'darkolivegreen', 'plum']

        # 背景颜色
        self.backgrounds = ['snow', 'seashell', 'oldlace',
                            'mintcream', 'ivory', 'honeydew', 'ghostwhite', 'cornsilk']

        # 字体文件路径
        self.font_path = os.path.join(
            app.root_path, 'fonts/FreeSansBoldOblique.ttf')

        # the words list maxlength = 8

        # creat a image
        im = Image.new('RGB', (self.img_width, self.img_height),
                       random.choice(self.backgrounds))
        self.code = self._yield_code()

        # 更具图片大小自动调整字体大小
        self.font_size = self._get_font_size()

        # creat a pen
        draw = ImageDraw.Draw(im)

        # 写验证码
        x = 8
        for i in self.code:
            # 上下抖动量,字数越多,上下抖动越大
            m = int(len(self.code))
            y = 2
            self.font = ImageFont.truetype(
                self.font_path, self.font_size + int(ceil(m)))
            draw.text((x, y), i, font=self.font,
                      fill=random.choice(self.font_color))
            x += self.font_size*self.rate

        width, height = im.size
        for k in range(2):
            randHeight = (height / 2) * (k % 2) + \
                random.randint(5, int((height - 1) / 2) - 5)
            startPoint = (0, randHeight)
            if random.random() < 0.5:
                endPoint = (width - 1, randHeight + random.randint(0, 5))
            else:
                endPoint = (width - 1, randHeight - random.randint(0, 5))
            draw.line((startPoint, endPoint), fill=(0, 0, 0, 255))

        del x
        del draw
        buf = BytesIO()
        im.save(buf, 'gif')
        self.captcha = buf.getvalue()
        buf.close()

    def check(self, code, expires_time):
        """
        检查用户输入的验证码是否正确
        """
        # _code = session.get(self.session_key) or ''
        # _expires_time = session.get(self.session_expire_key) or None
        if expires_time is not None:
            expires_time = arrow.get(expires_time)
        if expires_time and expires_time.shift(seconds=self.expires) > arrow.utcnow():
            return str(self.answer).lower() == str(code).lower()
        else:
            return False


class WorldCaptCha(Captcha):
    type = 'world'

    # 英文单词数字混合验证码
    def world(self):
        _words = r'1234567890abcdefghijklmnopqrstuvwxyz'
        code = ''.join(random.sample(_words, 4))
        self.answer = code
        return code


class RightNumberCaptCha(Captcha):
    type = 'right_number'
    # min、middle、max

    def right_number(self):
        while True:
            x = random.randrange(1, 30)
            y = random.randrange(1, 30)
            z = random.randrange(1, 30)
            if x != y and x != z and y != z:
                break
        r = random.choice([1, 2, 3])
        numbers = [x, y, z]
        if r == 1:
            answer = min(numbers)
            code = 'small: %s,%s,%s' % (x, y, z)
        elif r == 2:
            answer = sorted(numbers)[1]
            code = 'middle: %s,%s,%s' % (x, y, z)
        else:
            answer = max(numbers)
            code = 'big: %s,%s,%s' % (x, y, z)
        self.answer = answer
        return code
